package org.budo.maven.plugin.assembly.mojo.processor;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.budo.maven.plugin.assembly.mojo.util.MojoGitLogUtil;
import org.budo.support.lang.util.FileUtil;
import org.budo.support.lang.util.StringUtil;
import org.codehaus.plexus.util.xml.Xpp3Dom;

/**
 * @author lmw
 */
public class BudoMavenAssemblyLauncherProcessor extends AbstractBudoMavenAssemblyProcessor {
    public BudoMavenAssemblyLauncherProcessor(MavenProject project, MavenSession session, String format, Log log) {
        super(project, session, format, log);
    }

    @Override
    public void execute() throws Throwable {
        this.getLog().info("#31 BudoMavenAssemblyLauncherProcessor#execute project.artifact=" + this.getProject().getArtifact() //
                + ", project.base=" + this.getProject().getBasedir() + ", format=" + this.getFormat() + ", this=" + this);

        Properties activeProfileProperties = this.getActiveProfileProperties();

        // 清理目标文件夹
        String destPath = this.getProject().getBuild().getDirectory() + File.separator + this.getProject().getArtifactId() + "-assembly";
        FileUtil.deleteIfExists(destPath);

        // 复制依赖
        this.copyLib();

        // 复制conf
        this.copyConf(activeProfileProperties);

        // 写入git版本，到conf
        MojoGitLogUtil.writeGitLogToConf(this.getProject(), this.getLog());

        // 复制webapp
        this.copyWebapp();

        // 复制bin
        this.writeBin();

        // 复制 Docker
        this.writeDocker();

        // 模拟 maven-war-plugin 执行 资源占位符替换
        this.fakeMavenWarPluginWebResourceFiltering(activeProfileProperties);

        this.writeReadme();

        // 压缩
        this.compress();
    }

    private void fakeMavenWarPluginWebResourceFiltering(Properties activeProfileProperties) {
        List<Plugin> plugins = this.getProject().getBuildPlugins();
        for (Plugin plugin : plugins) {
            String artifactId = plugin.getArtifactId();
            if (StringUtil.equalsIgnoreCase(artifactId, "maven-war-plugin")) {
                this._fakeMavenWarPluginWebResourceFiltering(plugin, activeProfileProperties);
            }
        }
    }

    private void _fakeMavenWarPluginWebResourceFiltering(Plugin plugin, Properties activeProfileProperties) {
        Object configuration = plugin.getConfiguration();
        if (null == configuration) {
            this.getLog().error("#122 configuration is null, plugin=" + plugin);
            return;
        }

        if (!(configuration instanceof Xpp3Dom)) {
            this.getLog().error("#128 configuration is not Xpp3Dom, type=" + configuration.getClass() + ", configuration=" + configuration + ", plugin=" + plugin);
            return;
        }

        Xpp3Dom conf = (Xpp3Dom) configuration;
        Xpp3Dom webResources = conf.getChild("webResources");
        if (null == webResources) {
            this.getLog().error("#147 configuration no webResources, configuration=" + configuration + ", plugin=" + plugin);
            return;
        }

        Xpp3Dom[] resources = webResources.getChildren("resource");
        if (null == resources || resources.length < 1) {
            this.getLog().error("#153 webResources no resource, configuration=" + configuration + ", plugin=" + plugin);
            return;
        }

        for (Xpp3Dom resource : resources) {
            String filtering = resource.getChild("filtering").getValue();
            if (!"true".equalsIgnoreCase(filtering)) {
                continue;
            }

            String directory = resource.getChild("directory").getValue();
            Xpp3Dom includes = resource.getChild("includes");
            Xpp3Dom[] includeArray = includes.getChildren();
            for (Xpp3Dom xpp3Dom : includeArray) {
                String include = xpp3Dom.getValue();
                this.getLog().error("#157 directory=" + directory //
                        + ", directory.canonicalPath=" + FileUtil.getCanonicalPath(new File(directory)) //
                        + ", include=" + include //
                        + ", activeProfileProperties=" + activeProfileProperties);

                this.filterDir(new File(directory), include, activeProfileProperties);
            }
        }
    }

    private void filterDir(File file, String include, Properties activeProfileProperties) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File each : files) {
                this.filterDir(each, include, activeProfileProperties);
            }
        } else {
            if (include.startsWith("**/")) { // 暂时只支持这种配置
                if (file.getName().endsWith(include.substring(3))) {
                    File webInfDir = new File(this.getProject().getBuild().getDirectory() + File.separator //
                            + this.getProject().getArtifactId() + "-assembly" + File.separator + "webapp" + File.separator + "WEB-INF"); // target 中的 webapp 目录
                    File to = new File(webInfDir, file.getName());
                    this.filterFile(file, to, activeProfileProperties);
                }
            } else {
                // 其他配置抛异常
                throw new RuntimeException("#178 include=" + include);
            }
        }
    }

    private void copyWebapp() throws IOException, Throwable {
        String webappPath = "src" + File.separator + "main" + File.separator + "webapp";
        File srcWebapp = new File(this.getProject().getBasedir().getCanonicalPath() + File.separator + webappPath); // webapp 目录 绝对路径

        if (!srcWebapp.exists()) {
            this.getLog().warn("#174 folder " + webappPath + " not exists, path=" + srcWebapp.getCanonicalPath());
            return;
        }

        File webappDir = new File(this.getProject().getBuild().getDirectory() + File.separator //
                + this.getProject().getArtifactId() + "-assembly" + File.separator + "webapp"); // target 中的 webapp 目录

        this.getLog().info("#181 copy webapp, " + srcWebapp.getCanonicalPath() + " > " + webappDir.getCanonicalPath());
        FileUtil.copyDir(srcWebapp, webappDir);
    }

    private void copyConf(Properties activeProfileProperties) throws IOException, Throwable {
        List<Resource> resources = this.getProject().getResources();
        this.getLog().info("#233 resources=" + resources);

        File confDir = new File(this.getProject().getBuild().getDirectory() //
                + File.separator + this.getProject().getArtifactId() + "-assembly"//
                + File.separator + "conf"); // resources 复制到 conf

        for (final Resource resource : resources) {
            FileFilter fileFilter = new FileFilter() {
                public boolean accept(File file) {
                    if (BudoMavenAssemblyLauncherProcessor.this.matchAnyExcludes(file, resource)) {
                        return false; // 如果 match 就不复制
                    }

                    return true;
                }
            };

            File from = new File(resource.getDirectory());
            this.copyDirAndFiltering(from, confDir, fileFilter, resource.getFiltering(), activeProfileProperties);

            this.getLog().info("#224 copy conf, " + from.getCanonicalPath() + " > " + confDir.getCanonicalPath());
        }
    }

    /**
     * 将所有Maven依赖移到lib目录
     */
    private void copyLib() throws Throwable {
        MavenProject project = this.getProject();

        String target = project.getBuild().getDirectory();
        File libDir = new File(target + File.separator + project.getArtifactId() + "-assembly" + File.separator + "lib");

        Set<Artifact> projectArtifacts = project.getArtifacts();
        if (this.getLog().isDebugEnabled()) {
            this.getLog().debug("#199 copyLib, project.artifacts=" + projectArtifacts);
        } else {
            this.getLog().info("#201 copyLib, project.artifacts.size=" + (null == projectArtifacts ? null : projectArtifacts.size()));
        }

        for (Artifact projectArtifact : projectArtifacts) {
            this.getLog().info("#210 projectArtifact=" + projectArtifact + ", scope=" + projectArtifact.getScope());

            File artifactFile = projectArtifact.getFile();
            if (artifactFile.isDirectory()) { // workspace dependency
                Artifact localArtifact = this.getSession().getLocalRepository().find(projectArtifact);
                this.getLog().warn("#107 workspace dependency, artifactFile=" + artifactFile + ", localArtifact.file=" + localArtifact.getFile());

                artifactFile = localArtifact.getFile();
            }

            File to = new File(libDir, artifactFile.getName());
            this.getLog().info("#113 copy jar, " + artifactFile + " > " + to);
            FileUtil.copyFile(artifactFile, to);
        }
    }
}